#!/usr/bin/ruby

require 'dl/import'
require 'dl/struct'

# you need `MECAB_BIN' value

module Mecab
  module DL
    extend ::DL::Importable
  
    raise RuntimeError    unless defined?(MECAB_BIN)

    dl_name = case RUBY_PLATFORM
    when /mswin/
      "libmecab.dll"
    when /linux/
      "libmecab.so"
    else
      ""
    end

    dlload "#{MECAB_BIN}/#{dl_name}"

    typealias('size_t', 'long')

    extern "int           mecab_do (int, char **)"
    extern "mecab_t*      mecab_new(int, char **)"
    extern "mecab_t*      mecab_new2(const char *)"
    extern "const char*   mecab_version()"
    extern "const char*   mecab_strerror(mecab_t *)"
    extern "void          mecab_destroy(mecab_t *)"
    extern "int           mecab_get_partial(mecab_t *)"
    extern "void          mecab_set_partial(mecab_t *, int)"
    extern "float         mecab_get_theta(mecab_t *)"
    extern "void          mecab_set_theta(mecab_t *, float)"
    extern "int           mecab_get_lattice_level(mecab_t *)"
    extern "void          mecab_set_lattice_level(mecab_t *, int)"
    extern "int           mecab_get_all_morphs(mecab_t *)"
    extern "void          mecab_set_all_morphs(mecab_t *, int)"
    extern "const char*   mecab_sparse_tostr(mecab_t *, const char *)"
    extern "const char*   mecab_sparse_tostr2(mecab_t *, const char *, size_t)"
    extern "char*         mecab_sparse_tostr3(mecab_t *, const char *, size_t, char *, size_t)"
    extern "const mecab_node_t* mecab_sparse_tonode(mecab_t *, const char*)"
    extern "const mecab_node_t* mecab_sparse_tonode2(mecab_t *, const char*, size_t)"
    extern "const mecab_node_t* mecab_sparse_tonode2(mecab_t *, char*, int)"
    extern "const char*   mecab_nbest_sparse_tostr(mecab_t *, size_t, const char *)"
    extern "const char*   mecab_nbest_sparse_tostr2(mecab_t *, size_t, const char *, size_t)"
    extern "char*         mecab_nbest_sparse_tostr3(mecab_t *, size_t, const char *, size_t, char *, size_t)"
    extern "int           mecab_nbest_init(mecab_t *, const char *)"
    extern "int           mecab_nbest_init2(mecab_t *, const char *, size_t)"
    extern "const char*   mecab_nbest_next_tostr(mecab_t *)"
    extern "char*         mecab_nbest_next_tostr2(mecab_t *, char *, size_t)"
    extern "const mecab_node_t* mecab_nbest_next_tonode(mecab_t *)"
    extern "const char*   mecab_format_node(mecab_t *, const mecab_node_t *)"
    extern "const mecab_dictionary_info_t* mecab_dictionary_info(mecab_t *)"
    extern "int           mecab_dict_index(int, char **)"
    extern "int           mecab_dict_gen(int, char **)"
    extern "int           mecab_cost_train(int, char **)"
    extern "int           mecab_system_eval(int, char **)"
    extern "int           mecab_test_gen(int, char **)"

    MECAB_NOR_NODE = 0
    MECAB_UNK_NODE = 1
    MECAB_BOS_NODE = 2
    MECAB_EOS_NODE = 3

    MECAB_USR_DIC = 1
    MECAB_SYS_DIC = 0
    MECAB_UNK_DIC = 2

    typealias('const_char', 'const char')
    typealias('unsigned_int', 'unsigned int')
    typealias('unsigned_short', 'unsigned short')
    typealias('mecab_dictionary_info_t', 'struct mecab_dictionary_info_t')

    MECAB_DICTIONARY_INFO_T = struct [
      "const_char              *filename",
      "const_char              *charset",
      "unsigned_int            size",
      "int                     type",
      "unsigned_int            lsize",
      "unsigned_int            rsize",
      "unsigned_short          version",
      "mecab_dictionary_info_t *next",
    ]

    typealias('mecab_node_t', 'struct mecab_node_t')

    MECAB_PATH_T = struct [
      "mecab_node_t* rnode",
      "mecab_path_t* rnext",
      "mecab_node_t* lnode",
      "mecab_path_t* lnext",
      "int           cost",
      "float         prob",
    ]

    typealias('const_int', 'const int')
    typealias('mecab_learner_node_t', 'struct mecab_learner_node_t')

    MECAB_LEARNER_PATH_T = struct [
      "mecab_learner_node_t*  rnode",
      "mecab_learner_path_t*  rnext",
      "mecab_learner_node_t*  lnode",
      "mecab_learner_path_t*  lnext",
      "double                 cost",
      "const_int              *fvector",
    ]

    typealias('unsigned_short', 'unsigned short')

    MECAB_TOKEN_T = struct [
      "unsigned_short lcAttr",
      "unsigned_short rcAttr",
      "unsigned_short posid",
      "short          wcost",
      "unsigned_int   feature",
      "unsigned_int   compound",
    ]

    typealias('mecab_node_t', 'struct mecab_node_t')
    typealias('mecab_token_t', 'struct mecab_token_t')
    typealias('unsigned_char', 'unsigned char')

    MECAB_NODE_T = struct [
      "mecab_node_t   *prev",
      "mecab_node_t   *next",
      "mecab_node_t   *enext",
      "mecab_node_t   *bnext",
      "mecab_path_t   *rpath",
      "mecab_path_t   *lpath",
      "mecab_node_t   **begin_node_list",
      "mecab_node_t   **end_node_list",
      "const_char     *surface",
      "const_char     *feature",
      "unsigned_int   id",
      "unsigned_short length",
      "unsigned_short rlength",
      "unsigned_short rcAttr",
      "unsigned_short lcAttr",
      "unsigned_short posid",
      "unsigned_char  char_type",
      "unsigned_char  stat",
      "unsigned_char  isbest",
      "unsigned_int   sentence_length",
      "float          alpha",
      "float          beta",
      "float          prob",
      "short          wcost",
      "long           cost",
      "mecab_token_t  *token",
    ]

    typealias('mecab_learner_node_t', 'struct mecab_learner_node_t')

    MECAB_LEARNER_NODE_T = struct [
      "mecab_learner_node_t *prev",
      "mecab_learner_node_t *next",
      "mecab_learner_node_t *enext",
      "mecab_learner_node_t *bnext",
      "mecab_learner_path_t *rpath",
      "mecab_learner_path_t *lpath",
      "mecab_learner_node_t *anext",
      "const_char           *surface",
      "const_char           *feature",
      "unsigned_int         id",
      "unsigned_short       length",
      "unsigned_short       rlength",
      "unsigned_short       rcAttr",
      "unsigned_short       lcAttr",
      "unsigned_short       posid",
      "unsigned_char        char_type",
      "unsigned_char        stat",
      "unsigned_char        isbest",
      "double               alpha",
      "double               beta",
      "short                wcost2",
      "double               wcost",
      "double               cost",
      "const_int            *fvector",
      "mecab_token_t        *token",
    ]
  end

  class API
    def initialize(s = "")
      @mecab = Mecab::DL.mecab_new2(s)
    end

    def sparse_tostr(s, &block)
      result = Mecab::DL.mecab_sparse_tostr(@mecab, s)
    end

    def sparse_tonode(s, &block)
      result = Mecab::DL.mecab_sparse_tonode(@mecab, s)

      node = Mecab::DL::MECAB_NODE_T.new(result)
      while !node.nil?
        block.call(node)    unless block.nil?

        break  if node.next.nil?

        node = Mecab::DL::MECAB_NODE_T.new(node.next)
      end
    end

    def destroy()
      Mecab::DL.mecab_destroy(@mecab)
      @mecab = nil
    end
  end
end

